# !/usr/bin/env python
# -*- coding: utf-8 -*-
# ///////////////////////////////////////////////////////////////
# @File    :   baseApi.py
# @Time    :   2024/12/13 15:02:18
# @Author  :   kai.luo
# @Version :   1.0.0
# @Desc    :   None
# ///////////////////////////////////////////////////////////////

import re
import os
from PySide6.QtWidgets import *
from PySide6.QtGui import *
from PySide6.QtCore import *
from PySide6.QtSvg import *
import xml.etree.ElementTree as ET
from xml.dom import minidom
import subprocess


class QSSLoader(object):
    def __init__(self):
        pass

    @staticmethod
    def read_qss_file(qss_file_name):
        with open(qss_file_name, 'r',  encoding='UTF-8') as file:
            return file.read()


def setCenter(main_ui: QMainWindow):
    """
    将窗口置于屏幕居中
    Args:
        main_ui (_type_): _description_
    """
    screen_geometry = QApplication.primaryScreen().availableGeometry()
    # print(f'screen_h---{screen_geometry.width()}---{screen_geometry.height()}')
    newLeft = (screen_geometry.width() - main_ui.width()) / 2
    newTop = (screen_geometry.height() - main_ui.height()) / 2
    main_ui.move(int(newLeft), int(newTop))


# class Log(object):
#     def __init__(self, uiForm):
#         self.doc_linker_dict = {'4.1': '4.1 外设寄存器标题格式检查', '5.1': '5.1 外设寄存器基地址',
#                                 '5.2': '5.2 外设寄存器空间大小', '6.1': '6.1 功能寄存器标题的中文名称检查',
#                                 '6.2': '6.2 功能寄存器标题的英文名称检查', '6.3': '6.3 功能寄存器标题中文与英文名称一致性检查',
#                                 '6.4': '6.4 功能寄存器标题中的变量检查', '6.5': '6.5 功能寄存器标题中的变量数量检查',
#                                 '6.6': '6.6 功能寄存器标题中的变量特例1', '7.1': '7.1 寄存器的偏移地址有无检查',
#                                 '7.2': '7.2 偏移地址不包含变量的格式检查', '7.3': '7.3 偏移地址带变量的格式检查',
#                                 '7.4': '7.4 寄存器的复位值有无检查', '7.5': '7.5 寄存器的复位值格式检查',
#                                 '7.6': '7.6 偏移地址中公式检查', '8.2': '8.2 寄存器位的名称',
#                                 '8.3': '8.3 寄存器位的读写权限', '8.4': '8.4 寄存器详细说明表中的非保留位名必须加英文说明',
#                                 '8.5': '8.5 寄存器保留位', '8.6': '8.6 寄存器位表中位名个数与详细说明表中位名个数',
#                                 '8.7': '8.7 寄存器详细说明表中的变量', '8.8': '8.8 寄存器位表中位名与详细说明表中位名',
#                                 '8.9': '8.9 寄存器位表与详细说明表', '8.10': '8.10 寄存器位表特列1',
#                                 '8.11': '8.11 寄存器详细说明表特列1', '9.1': '9.1 表标题',
#                                 '9.2': '9.2 NVIC表“描述”需加英文说明', '9.3': '9.3 NVIC表中断字段检查',
#                                 '9.4': '9.4 NVIC表地址格式检查', '10.1': '10.1 文档标题检查',
#                                 '10.2': '10.2 文档版本号检查', '10.3': '10.3 文档中产品系列检查'}
#         #
#         self.INFO_style = f'【<span style="color:#1b21d1;">INFO***</span>】'  # 蓝色
#         self.WARNING_style = f'【<span style="color:#edaf00;">WARNING</span>】'  # 黄色
#         self.ERROR_style = f'【<span style="color:#eb1919;">ERROR**</span>】'  # 红
#         self.uiForm = uiForm

#     def printf(self, logType, logText, docText=None, logToDoc=None):
#         """
#         日志打印,self.uiForm: 主页面
#         logType: 日志类型:'消息','告警','错误'
#         logText: 日志内容
#         logToDoc: 日志到文档的链接,如果为空,为非极简打印模式
#         """
#         info_line_style = f'<span style="color:#1b21d1;">【{logText}】</span>'  # 消息文字样式
#         warning_line_style = f'<span style="color:#edaf00;">【{logText}】</span>'  # 告警文字样式
#         error_line_style = f'<span style="color:#eb1919;">【{logText}】</span>'  # 错误文字样式
#         docText_line_style = f'<span style="color:#eb1919;">{logText}</span>'  # 错误文字样式
#         linkerToDoc_line_style = f'<a href="{logToDoc}" style="color:#f12b2b;"><b>{logToDoc}</b></a>'  # 错误到原文的链接的样式
#         curtime = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))  # 当前时间

#         if self.uiForm.simplePrintLogBox.checkState() == Qt.Unchecked:  # 非极简模式
#             if logType == '消息':
#                 ouputInfo = f'{self.INFO_style} {curtime}: ' + info_line_style
#             elif logType == '告警':
#                 ouputInfo = f'{self.WARNING_style} {curtime}: ' + warning_line_style
#             else:
#                 if logToDoc:
#                     ouputInfo = f'{self.ERROR_style} {curtime}: ' + error_line_style + \
#                         f'【原文】- ->{docText_line_style}' + '>>>>>>>>' + linkerToDoc_line_style
#                 else:
#                     ouputInfo = f'{self.ERROR_style} {curtime}: ' + error_line_style
#         else:
#             if logType == '错误':
#                 if logToDoc:
#                     ouputInfo = f'{self.ERROR_style} {curtime}: ' + error_line_style + \
#                         f'【原文】- ->{docText_line_style}' + '>>>>>>>>' + linkerToDoc_line_style
#                 else:
#                     ouputInfo = f'{self.ERROR_style} {curtime}: ' + error_line_style
#             else:
#                 return
#         # 在指定的区域显示提示信息
#         self.uiForm.svdTextBrowser.append(ouputInfo)
#         cursot = self.uiForm.svdTextBrowser.textCursor()
#         self.uiForm.svdTextBrowser.moveCursor(cursot.End)
#         QApplication.processEvents()

#     def defaultPrint(self, logText):
#         """默认输出"""
#         ouputInfo = f'<span style="color:#020202;"> {logText} </span>'

#         # 在指定的区域显示提示信息
#         self.uiForm.svdTextBrowser.append(ouputInfo)
#         cursot = self.uiForm.svdTextBrowser.textCursor()
#         self.uiForm.svdTextBrowser.moveCursor(cursot.End)
#         QApplication.processEvents()


def printf(uiForm, type, contents, doc_text='', refer=''):
    """
    日志打印
    Args:
        uiForm (_type_): 主界面对象
        type (_type_): 类型，info:消息，error：错误，warming：警告，success：成功，plain：独占一行
        contents (_type_): _description_
    """
    info_line_style = f'<span style="color:#5d7599;">{contents}</span>'  # 消息文字样式
    error_line_style = f'<span style="color:#f56c6c;">{contents}</span>'  # 错误文字样式
    warning_line_style = f'<span style="color:#e1d839;">{contents}</span>'  # 告警文字样式
    success_line_style = f'<span style="color:#67c23a;">{contents}</span>'  # 成功文字样式
    plain_line_style = f'<span style="color:#1a5885;font-size:15px">{contents}</span>'  # 独占一行文字样式
    docText_line_style = f'<span style="color:#5d7599;">{doc_text}</span>'  # 原文指向文字样式
    refer_line_style = f'<span style="color:#5d7599;">{refer}</span>'  # 参考文档文字样式
    # linkerToDoc_line_style = f'<a href="{contents}" style="color:#f12b2b;"><b>{contents}</b></a>'  # 错误到原文的链接的样式
    # curtime = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))  # 当前时间
    pos_line = f'<span style="color:#246c26;font-size:14px">--->【原文指向】</span>'
    refer_line = f'<span style="color:#246c26;font-size:14px">--->【参考】</span>'
    if type == '消息':
        if not uiForm.log_mode_box.isChecked():  # 非极简模式
            type_line = f'【 <span style="color:#2d2e2e;font-size:14px;font-weight:600">{type}</span> 】'
            ouputLine = type_line + info_line_style
            if doc_text:
                ouputLine += pos_line+docText_line_style
            if refer:
                ouputLine += refer_line+refer_line_style
        else:
            return
    elif type == '告警':
        type_line = f'【 <span style="color:#e1d839;font-size:14px;font-weight:600">{type}</span> 】'
        ouputLine = type_line + warning_line_style
        if doc_text:
            ouputLine += pos_line+docText_line_style
        if refer:
            ouputLine += refer_line+refer_line_style
    elif type == '错误':
        type_line = f'【 <span style="color:#f56c6c;font-size:14px;font-weight:600">{type}</span> 】'
        ouputLine = type_line + error_line_style
        if doc_text:  # 需要指向原文位置
            ouputLine += pos_line+docText_line_style
        if refer:
            ouputLine += refer_line+refer_line_style
    elif type == '成功':
        type_line = f'【 <span style="color:#67c23a;font-size:14px;font-weight:600">{type}</span> 】'
        ouputLine = type_line+success_line_style
    else:
        ouputLine = plain_line_style

    # 在指定的区域显示提示信息
    tab_name = uiForm.tabWidget.tabText(uiForm.tab_index)
    if tab_name == 'Keil器件库':
        uiForm.keil_log_browser.append(ouputLine)
        cursot = uiForm.keil_log_browser.textCursor()
    else:
        uiForm.log_browser.append(ouputLine)
        cursot = uiForm.log_browser.textCursor()
    cursot.movePosition(QTextCursor.EndOfLine)
    QApplication.processEvents()


def TipDialog(windowTitle, defaultContext):
    """
    提示性弹框
    windowTitle: 标题类型（消息，警告，错误）
    defaultContext: 默认显示内容
    """
    dialog = QDialog()
    dialog.setProperty('name', 'Dialog')
    dialog.setWindowTitle(windowTitle.rjust(3, ' '))
    dialog.setWindowModality(Qt.ApplicationModal)
    dialog.setWindowIcon(QIcon('ui/logo.ico'))
    dialog.resize(260, 80)

    layout = QGridLayout(dialog)
    for i in range(10):
        layout.setColumnStretch(i, 1)
        layout.setRowStretch(i, 1)

    imgLabel = QLabel()
    if windowTitle == '消息':
        tips_img = QImage('ui/icons/info.png')
        tips_img = tips_img.scaled(30, 30, Qt.KeepAspectRatio)
    elif windowTitle == '警告':
        tips_img = QImage('ui/icons/warning.png')
        tips_img = tips_img.scaled(30, 30, Qt.KeepAspectRatio)
    elif windowTitle == '错误':
        tips_img = QImage('ui/icons/error.png')
        tips_img = tips_img.scaled(30, 30, Qt.KeepAspectRatio)
    else:
        tips_img = QImage('ui/icons/info.png')
        tips_img = tips_img.scaled(30, 30, Qt.KeepAspectRatio)
    imgLabel.setPixmap(QPixmap.fromImage(tips_img))

    tips_label = QLabel(defaultContext)
    tips_label.setStyleSheet("color:#0f375a")
    sure_btn = QPushButton('确定')
    sure_btn.setProperty('name', 'func_btn')

    layout.addWidget(imgLabel, 1, 0, 3, 2)
    layout.addWidget(tips_label, 1, 2, 4, 8)
    layout.addWidget(sure_btn, 9, 7, 3, 3)

    sure_btn.clicked.connect(dialog.accept)

    if dialog.exec_() == 1:
        dialog.close()
    else:
        dialog.close()


def tipMsgBox(msg, duration):
    # 创建一个 QMessageBox 弹框
    msg_box = QMessageBox()
    msg_box.setText(msg)
    msg_box.setStandardButtons(QMessageBox.Ok)

    # 显示消息框
    msg_box.show()

    # 设置一个定时器，2秒后自动关闭
    QTimer.singleShot(3000, msg_box.close)  # 2000ms = 2秒


def showProgress(uiForm, cur, num):
    """
    展示进度条
    Args:
        uiForm (_type_): 主界面
        cur (_type_): 当前进度
        num (_type_): 需要前进的总量
    """
    for i in range(1, num+1):
        uiForm.updateProgress(cur+i)


def setProgress(uiForm, num):
    """
   设置进度条
    Args:
        uiForm (_type_): 主界面
        cur (_type_): 当前进度
        num (_type_): 需要前进的总量
    """
    uiForm.updateProgress(num)


def hex2dec(hex_str):
    """
    16进制转10进制
    Args:
        hex_str (_type_): _description_
    """
    decimal_int = int(hex_str, 16)
    return decimal_int


def tohex(data, var_info):
    """
    :param: data:含变量的公式,0x04+8*(x-1),0x240+i*8+(x-1)*4，0x40 +（x-1）*0x20
    :param: var_info:含变量范围的字符串,i＝0..13， x=1..2
    :return: 输出16进制的数
    """
    # 将16进制的数都取出换成十进制
    hexList = re.findall(r'0x[0-9A-F]+', data)
    # 将中文括号转成英文
    data = re.sub(r'（', '(', data)
    data = re.sub(r'）', ')', data)
    hex_list = []
    for hexNum in hexList:
        data = re.sub(hexNum, str(eval(hexNum)), data)
    if '，' in var_info:
        var_str1, var_str2 = re.findall(r'[a-z]', data)
        # 寄存器有两个参数
        var_info1, var_info2 = var_info.split('，')
        start1, end1 = sorted(re.findall(r'\d+', var_info1))
        start2, end2 = sorted(re.findall(r'\d+', var_info2))
        # 判断变量
        for i in range(int(start1), int(end1) + 1):
            formula_str1 = data
            formula_str1 = re.sub(var_str1, str(i), formula_str1)
            for j in range(int(start2), int(end2) + 1):
                formula_str2 = formula_str1
                formula_str2 = re.sub(var_str2, str(j), formula_str2)
                intNum = eval(formula_str2)
                hex_list.append(hex(intNum))
    else:
        var_str = re.findall(r'[a-z]', data)
        start1, end1 = sorted(re.findall(r'\d+', var_info))
        for i in range(int(start1), int(end1) + 1):
            formula_str1 = data
            formula_str1 = re.sub(var_str[0], str(i), formula_str1)
            intNum = eval(formula_str1)
            hex_list.append(hex(intNum))
    return hex_list


def orderByRegister(peripheral_data):
    """
    根据外设中寄存器的偏移地址大小排序
    Args:
        peripheral_data (_type_): 外设数据列表
    """
    for peripheral in peripheral_data:
        if peripheral.get('name', '') in ['CAN1']:
            # 获取所有外设寄存器对应的偏移地址
            registers_list = peripheral.get('registers')
            for i in range(len(registers_list)):
                for j in range(len(registers_list)-i-1):
                    if hex2dec(registers_list[j].get('addressOffset')) > hex2dec(registers_list[j+1].get('addressOffset')):
                        registers_list[j], registers_list[j+1] = registers_list[j+1], registers_list[j]

    return peripheral_data


def delSpaceList(data: list):
    """
    去除二维数组中，子元素列表所有值都为空的列表
    Args:
        data (_type_): _description_
    """
    data_list = []
    for item in data:
        null_num = 0
        for i in range(len(item)):
            if item[i] == '':
                null_num += 1
        if null_num != len(item):
            data_list.append(item)

    return data_list


def showPdscFile(ui):
    """
    加载pdsc文件
    Args:
        path (str): _description_
    """
    try:
        file_name = 'config/examp.pdsc'
        # 读取并格式化 XML
        tree = ET.parse(file_name)  # 使用 xml.etree.ElementTree 读取 XML 文件
        root = tree.getroot()

        # 将 XML 转为字符串并格式化
        rough_string = ET.tostring(root, 'utf-8')  # 将元素转换为字节
        reparsed = minidom.parseString(rough_string)  # 使用 minidom 格式化
        xml_string = reparsed.toprettyxml(indent='  ')
        formatted_xml = '\n'.join([line for line in xml_string.splitlines() if line.strip()])

        # 在 QTextEdit 中显示格式化后的 XML
        ui.plainTextEdit.setPlainText(formatted_xml)
    except Exception as e:
        ui.plainTextEdit.setPlainText(f"无法加载 XML 文件: {e}")


def stdMoveFiles(file_type, path):
    pack_name = ''
    for root, dirs, files in os.walk(path):
        for file in files:
            if file_type in file:
                pack_name = file
                break
        break
    return pack_name


def updateDialog(mainUI, ok_btn, check_update_result):
    """
    功能性的提示弹框
    Args:
        tip_header (str): 弹框上的提示内容【发现有新版本，已是最新版本，资源数据需要更新】
        check_update_result (list): 有关更新内容的条目
    """
    dialog = QDialog()
    dialog.setWindowTitle('keil/iar器件库生成工具')
    dialog.setWindowModality(Qt.ApplicationModal)
    dialog.setWindowIcon(QIcon('ui/logo.ico'))
    dialog.resize(300, 150)

    local_version = check_update_result.get('local_version', '')
    remote_version = check_update_result.get('remote_version', '')
    # update_messages = check_update_result.get('messages', '')
    update_messages = check_update_result.get('messages', '')
    update_msgData = check_update_result.get('msgData', '')

    layout = QGridLayout(dialog)
    for i in range(15):
        layout.setColumnStretch(i, 1)
        layout.setRowStretch(i, 1)

    local_label = QLabel('当前版本:  ' + local_version)
    latest_label = QLabel('最新版本:  ' + remote_version)
    label = QLabel('更新内容: ')

    layout.addWidget(local_label, 1, 2, 1, 8)
    layout.addWidget(latest_label, 2, 2, 1, 8)
    update_btn = QPushButton(ok_btn)

    if ok_btn == '更新版本':
        layout.addWidget(label, 3, 2, 1, 8)
        update_messages_list = update_messages.split('\n')
        for index, item in enumerate(update_messages_list):
            update_labels = QLabel(item)
            layout.addWidget(update_labels, 4+index, 5, 1, 8)
        update_btn.clicked.connect(lambda: callUpdate(mainUI, dialog))
    elif ok_btn == '更新资源数据':
        layout.addWidget(label, 3, 2, 1, 8)
        update_msgData_list = update_msgData.split('\n')
        for index, item in enumerate(update_msgData_list):
            update_labels = QLabel(item)
            layout.addWidget(update_labels, 4+index, 5, 1, 8)
        update_btn.clicked.connect(lambda: callUpdate(mainUI, dialog))

    layout.addWidget(update_btn, 13, 13, 2, 1)
    if dialog.exec_() == 1:
        okPressed = True
    else:
        okPressed = False


def callUpdate(mainUI, dialog):
    # 路径
    user_documents_path = os.path.expanduser('~\\Documents\\HK_Keil_Iar')
    os.makedirs(user_documents_path, exist_ok=True)
    updaterExe_path = os.path.join(user_documents_path, 'Updater.exe')

    # 调用升级软件
    if not os.path.isfile(updaterExe_path):
        return TipDialog('错误', f'用户目录 {user_documents_path} 中未发现 Updater.exe.')

    mainUI.close()
    dialog.accept()
    subprocess.Popen(updaterExe_path, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
